# -*- coding: utf-8 -*-
# author : hxt
#   date : 2025/2/20
'''
    这是接口关键字驱动类，用于提供自动化接口测试的关键字方法。
    主要是实现常用的关键字内容，并定义好所有的参数内容即可
    接口中常用关键字：
        1.各种模拟请求方法：Post/get/put/delete/header/....
        2.根据需求进行断言封装：jsonpath、数据库断言
        3.集合Allure，可添加@allure.step，这样在自动化执行的时候
        Allure报告可以直接捕捉相关的执行信息，让测试报告更详细
'''
import allure
import jsonpath
import pymysql
import requests
import random
# 导入加密的包
# from crypto.Cipher import AES
# # from deepdiff import DeepDiff
# from crypto.Util.Padding import pad, unpad
# import base64
from deepdiff import DeepDiff
from pymysql import cursors

from apirun.core.globalContext import g_context


class KeyWords:
    # def __init__(self): #若需要保持会话，添加此构造方法即可
    #     request = requests.Session()

    @allure.step("参数数据：发送Post请求")
    def request_post(self, **kwargs):
        res = requests.post(**kwargs)
        # TODO: 扩展- 把对应的响应数据写到变量渲染中
        g_context().set_dict("current_response", res)  # 默认设置成变量渲染
        print("-------------------------------------------------")
        print('响应结果 -- ：',res.json())
        print("-------------------------------------------------")
        return res

    @allure.step('参数数据：发送Post-form_urlencoded请求')
    def request_post_form_urlencoded(self,**kwargs):
        '''
        post请求，data形式
        :param url:
        :param headers:
        :param params:
        :param data:
        :return:
        '''
        url = kwargs.get('url',None)
        headers = kwargs.get('headers',None)
        params = kwargs.get('params',None)
        data = kwargs.get('data',None)
        request_data = {
            "url": url,
            "params": params,
            "headers": headers,
            "data": data,
        }
        res = requests.post(**request_data)
        g_context().set_dict("current_response", res)
        print("-------------------------------------------------")
        print('响应结果 -- ：',res.json())
        print("-------------------------------------------------")
        return res

    @allure.step('参数数据：发送Post-raw_json请求')
    def request_post_raw_json(self, **kwargs):
        '''
        post请求，json形式
        :param url:
        :param headers:
        :param params:
        :param data:
        :return:
        '''
        url = kwargs.get('url', None)
        headers = kwargs.get('headers', None)
        params = kwargs.get('params', None)
        data = kwargs.get('data', None)
        request_data = {
            "url": url,
            "params": params,
            "headers": headers,
            "json": data,
        }
        res = requests.post(**request_data)
        print('-----,,,',res.request.body)
        g_context().set_dict("current_response", res)
        print("-------------------------------------------------")
        print('响应结果 -- ：', res.json())
        print("-------------------------------------------------")
        return res

    @allure.step('参数数据：发送get请求')
    def request_get(self, **kwargs):
        '''
        get请求
        :param url:
        :param headers:
        :param params:
        :return:
        '''
        url = kwargs.get('url', None)
        headers = kwargs.get('headers', None)
        params = kwargs.get('params', None)
        request_data = {
            "url": url,
            "params": params,
            "headers": headers

        }
        res = requests.get(**request_data)
        g_context().set_dict("current_response", res)
        print("-------------------------------------------------")
        print('响应结果 -- ：', res.json())
        print("-------------------------------------------------")
        return res

    @allure.step("参数数据：发送Post请求-form_data")
    def request_post_form_data(self, **kwargs):
        '''
        post请求，上传文件
        :param url:
        :param headers:
        :param params:
        :param data:
        :param files: 上传的文件数据，格式为：{'file':open('图片路径','rb')}
        :return:
        '''
        url = kwargs.get("url", None)
        files = kwargs.get("files", None)
        params = kwargs.get("params", None)
        headers = kwargs.get("headers", None)
        data = kwargs.get("data", None)

        request_data = {
            "url": url,
            "params": params,
            "headers": headers,
            "data": data,
            "files": eval(files)  # 变成字典格式
        }

        res = requests.post(**request_data)
        print("response", res.json())
        g_context().set_dict("current_response", res)  # 默认设置成全局变量
        print("-------------------------------------------------")
        print('响应结果 -- ：', res.json())
        print("-------------------------------------------------")
        return res

    @allure.step("参数数据：发送Delete请求")
    def request_delete(self, **kwargs):
        '''
         delete请求
         :param url:
         :param headers:
         :param params:
         :param data:
         :return:
         '''
        url = kwargs.get("url", None)
        params = kwargs.get("params", None)
        headers = kwargs.get("headers", None)
        data = kwargs.get("data", None)

        request_data = {
            "url": url,
            "params": params,
            "headers": headers,
            "json": data,
        }

        res = requests.delete(**request_data)
        g_context().set_dict("current_response", res)  # 默认设置成全局变量
        print("-------------------------------------------------")
        print('响应结果 -- ：', res.json())
        print("-------------------------------------------------")
        return res

    @allure.step('参数数据：提取json数据并保存到全局变量')
    def ex_json_data(self, **kwargs):
        '''
        提取Json数据
        1、提取的数据是什么
        2、提取表达式是什么
        3、提取的下标是什么
        4、保存到全局变量变量名是什么
        :param exvalue:提取json的表达式
        :param index:下标，默认为0
        :param varname:存储到全局变量的变量名
        :return:
        '''
        # 1、拿到响应数据
        res = g_context().get_dict("current_response")
        # 2、拿到表达式
        exvalue = kwargs.get('exvalue',None)
        # 3、拿到下标
        index = kwargs.get('index',0)
        if index is None:
            index = 0
        if exvalue is None:
            index = 0

        # 提取数据
        ex_data = jsonpath.jsonpath(res.json(),exvalue)[index]

        # 4、通过提取数据存储到全局变量
        g_context().set_dict(kwargs["varname"],ex_data)
        print("-------------------------------------------------")
        print('全局变量为 -- ',g_context().show_dict())
        print("-------------------------------------------------")
        return ex_data

    @allure.step('参数数据：提取Mysql数据库数据并保存到全局变量')
    def ex_mysql_data(self, **kwargs):
        """
        查询对应的数据并且存储到对应的全局变量当中。
        :param 数据库: 你要使用的数据库的名字
        :param sql: 你要执行的SQL
        :param 引用变量: 变量名 : 多个逗号分割
        :return:
        """
        db_config = g_context().get_dict('_database')[kwargs['数据库']]
        print(db_config)
        config = {"cursorclass": cursors.DictCursor}    #数据库执行后，以字典形式返回数据；若不指定，默认返回元组
        config.update(db_config)
        connection = pymysql.connect(**config)
        cur = connection.cursor()
        cur.execute(kwargs['sql'])

        res = cur.fetchall()
        cur.close()

        var_names = kwargs['引用变量'].split(',')

        result = {}  #对查询结果数据进行处理，以变量加下标 存储到全局变量
        for i,data in enumerate(res,start=1):
            for j,name in enumerate(var_names):
                result[f'{name}_{i}'] = data.get(name)

        g_context().set_by_dict(result)
        print("-------------------------------------------------")
        print('全局变量为 -- ',g_context().show_dict())
        print("-------------------------------------------------")

    @allure.step('参数数据：断言当前文本内容')
    def assert_text_comparators(self,**kwargs):
        '''
        封装断言方法
        :param expected: 期望结果
        :param value: 实际结果
        :param op_str: 比较运算符 > == <
        :return:
        '''
        # 字典 + 匿名函数
        comparators = {
            '>': lambda a,b:a > b,
            '==':lambda a,b:a == b,
            '<':lambda a,b:a < b,
            '>=':lambda a,b:a >= b,
            '<=':lambda a,b:a <= b,
            '!=':lambda a,b:a != b,

        }

        if kwargs['op_str'] not in comparators:
            raise ValueError(f"没有该操作方式: {kwargs['OP_STR']}")

        if not comparators[f"{kwargs['op_str']}"](kwargs['value'],kwargs['expected']):
            raise AssertionError(f"{kwargs['value']} {kwargs['op_str']} {kwargs['expected']} 失败")

    @allure.step('参数数据：')
    def assert_json_comparators(self,**kwargs):
        '''
        封装断言方法
        :param expected: 期望结果
        :param value: 要比较的jsonPath值
        :param op_str: 比较运算符 > == <
        :param message (str, optional): 自定义的错误消息。
        :return:
        '''
        # 字典 + 匿名函数
        comparators = {
            '>': lambda a,b:a > b,
            '==':lambda a,b:a == b,
            '<':lambda a,b:a < b,
            '>=':lambda a,b:a >= b,
            '<=':lambda a,b:a <= b,
            '!=':lambda a,b:a != b,

        }

        message = kwargs.get('message',None)

        if kwargs['op_str'] not in comparators:
            raise ValueError(f"没有该操作方式: {kwargs['OP_STR']}")

        re = g_context().get_dict('current_response').json()

        ex_data = jsonpath.jsonpath(re,kwargs['value'])[0]

        if not comparators[f"{kwargs['op_str']}"](ex_data,kwargs['expected']):
            if message:
                raise AssertionError(message)
            else:
                 raise AssertionError(f"{ex_data} {kwargs['op_str']} {kwargs['expected']} 失败")

    # TODO: 扩展 - 全量断言-对比两个Json的差异
    @allure.step("参数数据：全量断言-对比两个Json的差异")
    def assert_json_deepdiff(self, **kwargs):
        """
        对比两个json的差异
        :param expected: 期望结果
        :param actual: 实际结果
        :param exclude_paths:需要排除的字段，集合的类型，比如{“id”,...}
        :param ignore_order: 忽略顺序，一般用户有序数据类型，比如列表
        :param ignore_string_case:忽略值的大小写，False
        :return: 当数据没有差异则返回空集合
        """

        expected = kwargs['expected']
        actual = kwargs['actual']

        exclude_paths = kwargs.get('kwargs',None)
        ignore_order = kwargs.get('ignore_order',False)
        ignore_string_case = kwargs.get('ignore_string_case', False)

        screen_data = {
            'exclude_paths':exclude_paths,
            'ignore_order': ignore_order,
            'ignore_string_case': ignore_string_case
        }

        diff_data = DeepDiff(expected,actual,**screen_data)

        assert not diff_data,f'全量断言失败，{diff_data}'


    # # TODO: 扩展 - 加密处理
    # @allure.step("参数数据：对数据进行AES加密处理")
    # def encrypt_aes(self, **kwargs):
    #     """
    #     对数据进行AES加密
    #     :param data: 需要加密的数据
    #     :param VARNAME: 存储到全局变量的名称
    #     :return:
    #     """
    #     key = b"1234567812345678"  # key 密码
    #     data = kwargs["data"].encode('utf-8')
    #     cipher = AES.new(key, AES.MODE_ECB)  # 使用ECB模式
    #     ct_bytes = cipher.encrypt(pad(data, AES.block_size))  # 使用PKCS7填充,初始化数据块大小, 16位
    #     encrypt_data = base64.b64encode(ct_bytes).decode('utf-8')
    #
    #     g_context().set_dict(kwargs["VARNAME"], encrypt_data)  # 根据变量名设置成变量
    #     print("-----------------------")
    #     print(g_context().show_dict())
    #     print("-----------------------")

    # ----------------------实战扩展方法------------------------------
    @allure.step("参数数据：对数据进行AES加密处理")
    def generate_name(self, **kwargs):
        '''

        :param varname:
        :return:
        '''
        data = "hami" + str(random.randint(0, 9999))
        g_context().set_dict(kwargs["varname"], data)  # 根据变量名设置成变量
